# -*- coding: utf-8 -*-
"""
Created on Sat Oct 19 09:54:23 2019

@author: shosborn
"""

#!/usr/bin/python3

import numpy
import CERTMT_runScenario as scenario
import sys
import csv
import os.path
from multiprocessing.pool import ThreadPool
import datetime

BASELINE = scenario.BASELINE
CERT_MT = scenario.CERT_MT


CORE_COUNT = int(sys.argv[1])
EXPECTED_SCORE = float(sys.argv[2])
SLOPE = float(sys.argv[3])
SCORE_DISTRO = int(sys.argv[4])
PERIOD_COUNT = int(sys.argv[5])
MIN_SAMPLES = int(sys.argv[6])
FILE_OUT = sys.argv[7]
util_range_low = float(sys.argv[8])
util_range_high = float(sys.argv[9])



# Superseded by command line parameter
#CORE_COUNT=[4, 6, 8]

#Testing
'''
MIN_TOTAL_UTIL=3
MAX_TOTAL_UTIL=5
BIN_SIZE=.5
UTIL_RANGES=[(.3, .7)]
PERIOD_MIN=10
'''


MAX_TOTAL_UTIL = 2*CORE_COUNT
BIN_SIZE = .25
MIN_TOTAL_UTIL = 3*CORE_COUNT/4
#UTIL_RANGES = [(0, .4), (.3, .7), (.6, 1), (0, 1)] #low, medium, high, wide
PERIOD_MIN = 10
PROB_HARM = 0
DEGREE_HARM = 2
FILE_STATUS = "status.txt"
#SCORE_DISTRO = (scenario.FIXED, scenario.EXPO)
#EXPECTED_SCORE_TUPLE = (EXPECTED_SCORE,)

# dictionary keys need to match paramters in runScenario
#def runParamTest(maxTotalUtil, minTotalUtil, bin_size, min_samples,
#                 utilMin, utilMax, expected_score, slope, prob_harm, degree_harm, score_distro, core_count, periodMin, periodCount, timeout):
configurations = []
config = {}
config["minTotalUtil"] = MIN_TOTAL_UTIL
config["maxTotalUtil"] = MAX_TOTAL_UTIL
config["bin_size"] = BIN_SIZE
config["min_samples"] = MIN_SAMPLES
config["utilMin"] = util_range_low
config["utilMax"] = util_range_high
config["expected_score"] = EXPECTED_SCORE
config["slope"] = SLOPE
config["prob_harm"] = PROB_HARM
config["degree_harm"] = DEGREE_HARM
config["score_distro"] = SCORE_DISTRO
config["periodMin"] = PERIOD_MIN
config["periodCount"] = PERIOD_COUNT
config["core_count"] = CORE_COUNT
config["timeout"] = 60
configurations.append(config)

# Run mode configuration
SYNC = False


def calculate_rsa(theBins, algo, bin_width, minTotalUtil, core_count):
    # total_samples = theBins[0]
    total_area = minTotalUtil
    # iterate through theBins
    for b in theBins:
        if b[0] > 0:
            total_area += (b[algo] / b[0]) * bin_width
    return total_area / core_count

def save_scenario_results(results, maxTotalUtil, minTotalUtil, bin_size, min_samples,
                 utilMin, utilMax, expected_score, slope, prob_harm, degree_harm, score_distro, core_count, periodMin, periodCount, timeout):
    parameter_str = ",".join([str(core_count),
                             str(utilMin),
                             str(utilMax),
                             str(expected_score),
                             str(score_distro),
                             str(slope),
                             str(periodCount),
                             str(timeout),
                             str(bin_size)
                             ])
    # timing data
    #success_summary_string = ",".join(results[scenario.INDEX_SUCCESS_SUMMARY])
    #failure_summary_string = ",".join(results[scenario.INDEX_FAIL_SUMMARY])
    #failure_count_string = ",".join(results[scenario.INDEX_ALL_FAIL_COUNTERS])
    # calculate RSA
    success_summary_string = ""
    for s in results[scenario.INDEX_SUCCESS_SUMMARY]:
        success_summary_string += str(s) + ","

    failure_summary_string = ""
    for f in results[scenario.INDEX_FAIL_SUMMARY]:
        failure_summary_string += str(f) + ","

    failure_count_string = ""
    for c in results[scenario.INDEX_ALL_FAIL_COUNTERS]:
        failure_count_string += str(c) + ","

    output_string = parameter_str + "," + success_summary_string + failure_summary_string + failure_count_string

    rsa = calculate_rsa (results[scenario.INDEX_THE_BINS], CERT_MT, BIN_SIZE, minTotalUtil, core_count)
    # get just the values I want from theBins; not using baseline
    bin_string = ""
    for b in results[scenario.INDEX_THE_BINS]:
        if b[0] > 0:
            bin_string = bin_string + "," + str(b[CERT_MT] / b[0])
        else:  # prevent divide-by-zero
            bin_string = bin_string + ",0"
    # combine all the strings
    #output_string = ",".join((parameter_str, success_summary_string, failure_summary_string, failure_count_string, bin_string, ","))
    output_string = output_string + str(rsa) + bin_string + ","
    # print the output string
    with open(FILE_OUT, "a") as f:
        print(output_string, file=f)



#change to match my parameters
def run_test(configuration):
    results=scenario.runParamTest(**configuration)
    save_scenario_results(results, **configuration)



    parameter_str = ",".join((str(configuration['core_count']),
                             str(configuration['minUtil']),
                             str(configuration['maxUtil']),
                             str(configuration['expected_score']),
                             str(configuration['score_distro']),
                             str(configuration['slope']),
                             str(configuration['periodCount']),
                             str(configuration['timeout']),
                             str(BIN_SIZE))
                             )
    # timing data
    success_summary_string = ",".join(results[scenario.INDEX_SUCCESS_SUMMARY])
    failure_summary_string = ",".join(results[scenario.INDEX_FAIL_SUMMARY])
    failure_count_string = ",".join(results[scenario.INDEX_ALL_FAIL_COUNTERS])
    # calculate RSA
    rsa = calculate_rsa (results[scenario.INDEX_THE_BINS], CERT_MT, BIN_SIZE, configuration['minTotalUtil'])
    # get just the values I want from theBins; not using baseline
    bin_string = ""
    for b in results[scenario.INDEX_THE_BINS]:
        if b[0] > 0:
            bin_string = bin_string + "," + str(b[CERT_MT] / b[0])
        else:  # prevent divide-by-zero
            bin_string = bin_string + ",0"
    # combine all the strings
    output_string = ",".join((parameter_str, success_summary_string, failure_summary_string, failure_count_string, bin_string, ","))
    # print the output string
    with open(FILE_OUT, "a") as f:
        print(output_string, file=f)

# We need this protected so that subprocesses do not run it
if __name__ == '__main__':
    #timings = [0] * 3
    # Use multithreading
    with ThreadPool(processes=len(configurations)) as pool:
        # If we do not need the results to be in order, save things whenever we get them
        for res_tup in pool.imap_unordered(run_test, configurations):
            pass
            # each result gives data corresponding to one graph

    #print(datetime.datetime.now(), FILE_OUT, "Complete!\nTimings:")
    #print("\tBaseline:", timings[0]/len(configurations))
    #print("\tSMT 1-Frame:", timings[1]/len(configurations))
    #print("\tCERT-MT:", timings[2]/len(configurations))
    #print("\tFully Preemptive:", timings[3]/len(configurations))
